package com.glodon.paas.job.agent.task;

import static com.glodon.paas.job.JobConstants.*;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;

import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.glodon.paas.job.JobConstants;
import com.glodon.paas.job.agent.util.StoreUtil;
import com.glodon.paas.job.agent.worker.Caller;
import com.glodon.paas.job.agent.worker.Worker;
import com.glodon.paas.job.agent.worker.WorkerException;
import com.glodon.paas.job.model.Task;
import com.glodon.paas.job.util.DateFormatUtil;

@SuppressWarnings({ "unchecked", "rawtypes" })
public class TaskListener extends AbstractTaskListener {
    public static final Logger logger = LoggerFactory.getLogger(TaskListener.class);
    private String folder;
    private Random random = new Random();

    public TaskListener() {
        folder = System.getProperty("java.io.tmpdir");
    }

    @Override
    protected void onTask(Task task, Worker worker, Caller caller) throws WorkerException {
        List<File> downloadInputFile = Collections.EMPTY_LIST;
        File outputFolder = null;
        try {
            // begin  download beginning time
            Date downLoadBeginTime = task.getStartTime();
            long start = System.currentTimeMillis();
            // end
            Boolean online = (Boolean) task.getValue("online");
            if (!Boolean.TRUE.equals(online)) {
            	task.addValue(PROPERTY_TASK_DOWNLOADBEGIN_TIME, DateFormatUtil.format(downLoadBeginTime));
                downloadInputFile = downloadInputFile(task);
                Date downLoadEndTime = now(task.getStartTime(), start);
                task.addValue(PROPERTY_TASK_DOWNLOADEND_TIME, DateFormatUtil.format(downLoadEndTime));
            } else {
                List<String> urls = new ArrayList<String>();
                Map<String, String> input = (Map<String, String>) task.getValue(INPUT);
                for (Entry<String, String> entry : input.entrySet()) {
                    String url = entry.getValue();
                    urls.add(url);
                }
                // begin add filesize
                task.addValue(ORIGIN_INPUT, task.getValue(INPUT));
                task.addValue(INPUT, urls);
                
                if(!urls.isEmpty()){
                	for(String url:urls){
                		String[]  urlArray=url.split("[?]");
                		if(urlArray.length>1){
                			String[]  fileNameArray=urlArray[0].split("[/]");
                			String originFilename=fileNameArray[fileNameArray.length-1];
                			task.addValue(JobConstants.ORIGIN_FILENAME, originFilename);
                			task.setOriginFilename(originFilename);
                			break;
                		}
                	}
                }
            }

            Object output = task.getValue(OUTPUT);
            String outputUrl = null;
            if (output != null) {
                outputFolder = createOutputFolder();
                outputUrl = (String) output;
                task.addValue(ORIGIN_OUTPUT, task.getValue(OUTPUT));
                task.addValue(OUTPUT, outputFolder);
            }
            // begin  caculation beginning time
            Date caculationTime = now(task.getStartTime(), start);
            task.addValue(PROPERTY_TASK_CACULATIONBEGIN_TIME, DateFormatUtil.format(caculationTime));
            // end 
            Map result = worker.work(task);
            // begin caculation end time
            Date caculationEndTime = now(task.getStartTime(), start);
            task.addValue(PROPERTY_TASK_CACULATIONEND_TIME, DateFormatUtil.format(caculationEndTime));
            // end
            task.addValues(result);
            if (output != null) {
                // begin upload beginning time
                Date uploadBeginTime = now(task.getStartTime(), start);
                task.addValue(PROPERTY_TASK_UPLOADBEGIN_TIME, DateFormatUtil.format(uploadBeginTime));
                // end
                uploadOutputFile(outputFolder, outputUrl);
                Date upLoadEndTime = now(task.getStartTime(), start);
                task.addValue(PROPERTY_TASK_UPLOADEND_TIME, DateFormatUtil.format(upLoadEndTime));
                File[] files = outputFolder.listFiles();
                if (files.length > 0)
                    task.addValue("filename", files[0].getName());
            }
            logger.info("work complete");
        } catch (IOException e) {
            throw new WorkerException(e);
        } finally {
            for (File file : downloadInputFile) {
                if (!FileUtils.deleteQuietly(file))
                    logger.info("can't delete file: {}", file);
                file.getParentFile().delete();
            }
            if (!FileUtils.deleteQuietly(outputFolder))
                logger.info("can't delete folder: {}", outputFolder);
        }
    }

    private Date now(Date startTime, long start) {
        return new Date(startTime.getTime() + System.currentTimeMillis() - start);
    }

    private File createOutputFolder() {
        File outputFolder = new File(folder, "output" + Math.abs(random.nextLong()));
        if (!outputFolder.exists())
            outputFolder.mkdirs();
        return outputFolder;
    }

    private void uploadOutputFile(File outputFolder, String outputUrl) throws IOException {
        logger.info("upload output file: folder: {}, url: {}", outputFolder, outputUrl);
        uploadFolder(outputFolder, outputFolder, outputUrl);
    }

    private void uploadFolder(File base, File outputFolder, String url) throws IOException {
        File[] files = outputFolder.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                uploadFolder(base, file, url);
            } else {
                upload(base, file, url);
            }
        }
    }

    private void upload(File base, File file, String url) throws IOException {
        URI relativize = base.toURI().relativize(file.toURI());
        String path = "/" + relativize;
        StoreUtil.upload(getUrl(url, path), file);
    }

    private String getUrl(String url, String path) {
        int paramIndex = url.indexOf('?');
        return paramIndex < 0 ? url + path : url.substring(0, paramIndex) + path + "?" + url.substring(paramIndex + 1);
    }

    private List<File> downloadInputFile(Task task) throws IOException {
        List<String> names = new ArrayList<String>();
        Map<String, String> input = (Map<String, String>) task.getValue(INPUT);
        if (input == null)
            return Collections.EMPTY_LIST;
        // begin define total file size
        long totalSize = 0L;
        final List<File> downloadFiles = new ArrayList<File>();
        // end
        for (Entry<String, String> entry : input.entrySet()) {
            String name = entry.getKey();
            String url = entry.getValue();
            File file = new File(folder, name);
            file = StoreUtil.download(url, file, task);
            downloadFiles.add(file);
            // begin count size
            totalSize += file.length();
            // end
            names.add(file.getAbsolutePath());
        }
        // begin add filesize
        task.addValue(ORIGIN_INPUT, task.getValue(INPUT));
        task.addValue(INPUT, names);
        task.addValue(JOB_FILESIZE, totalSize);
        return downloadFiles;
        // end
    }

}
